// STL
#include <limits>                                 // std::numeric_limits<>
#include <vector>                                 // std::vector<>

// jSCIENCE
#include "jrandnum.hpp"                           // rand_num_uniform_Mersenne_twister()

// this
#include "statsxx/optimization/EA/EAParam.hpp"    // EAParam
#include "statsxx/optimization/EA/fitness.hpp"    // calc_shared_fitness()
#include "statsxx/optimization/EA/Individual.hpp" // Individual
#include "statsxx/optimization/EA/Population.hpp" // Population


static Individual find_winner(
                              const std::vector<Individual> &participants

                              );

//
// DESC: Selects an individual from a population using tournament selection. The current offspring of the NEXT generation is passed, as this is used in fitness sharing.
//
// NOTES:
//     ! Naive fitness sharing does NOT work with tournament selection, where we calculate fitnesses based on a current population, which is why the offspring are passed to this routine.
//     ! See:
//          C. K. Oei, D. E. Goldberg, and S.-J. Chang, "Tournament Selection, Niching, and the Preservation of Diversity", (1991)
//
inline Individual tournament_select(
                                    const Population &population,
                                    const Population &offspring,
                                    // -----
                                    const EAParam &param
                                    )
{
    std::vector<Individual> participants;

    for( int i = 0; i < param.tournament_nparticipants; ++i )
    {
        int idx = rand_num_uniform_Mersenne_twister(0, (population.individuals.size()-1));

        participants.push_back( population.individuals[idx] );
    }

    if( param.fitness_sharing )
    {
        for( auto &indiv : participants )
        {
            indiv.fitness = calc_shared_fitness(indiv, offspring, param);
        }
    }

    return find_winner(participants);
}


//
// DESC: Given a list of tournament participants, find the winner.
//
// TODO: Could (according to Wiki) be able to me more sophisticated here by selection the best individuals with some probability p.
//
// TODO: This subroutine could be written MUCH cleaner, using *::min_element() and ::distance().
//
static inline Individual find_winner(
                                     const std::vector<Individual> &participants
                                     )
{
    int best_idx        = -1;
    double best_fitness = std::numeric_limits<double>::lowest();

    for( decltype(participants.size()) i = 0; i < participants.size(); ++i )
    {
        if( participants[i].fitness > best_fitness )
        {
            best_fitness = participants[i].fitness;
            best_idx     = i;
        }
    }

    return participants[best_idx];
}


